home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / DevCon / Milan_1991 / Devcon91.4 / AppShell / Examples / Timer / timer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-01  |  20.8 KB  |  627 lines

  1. /*
  2.  * timer.c
  3.  *
  4.  * Appshell Timer Handler
  5.  * Copyright (C) 1991 Kompositor Software
  6.  * Written by John F Wiederhirn
  7.  *
  8.  * Based on the AppShell Notification Handler
  9.  * Copyright (C) 1990 Commodore-Amiga, Inc.
  10.  * Written by David N. Junod
  11.  *
  12.  */
  13.  
  14. /* The normal AppShell and system of includes... */
  15.  
  16. #include <exec/exec.h>
  17. #include <devices/timer.h>
  18. #include <libraries/appshell.h>
  19. #include <exec/types.h>
  20. #include <exec/memory.h>
  21. #include <exec/libraries.h>
  22. #include <intuition/intuition.h>
  23. #include <graphics/gfx.h>
  24. #include <libraries/gadtools.h>
  25. #include <libraries/appshell.h>
  26. #include <utility/tagitem.h>
  27. #include <clib/alib_protos.h>
  28. #include <clib/alib_stdio_protos.h>
  29. #include <clib/exec_protos.h>
  30. #include <clib/intuition_protos.h>
  31. #include <clib/graphics_protos.h>
  32. #include <clib/gadtools_protos.h>
  33. #include <clib/appshell_protos.h>
  34. #include <clib/utility_protos.h>
  35. #include <pragmas/appshell.h>
  36. #include <string.h>
  37. #include "ae:support/misc_protos.h"
  38.  
  39. #include "timer.h"
  40.  
  41. extern struct Library *AppShellBase;
  42.  
  43. /* Grabbing this will embed the version information generated */
  44. /* by BUMPREV in the make process into the actual executable. */
  45. #include "timer_rev.h"
  46.  
  47. /* This is the global Timer handler information packet. */
  48. struct TimerInfo
  49. {
  50.     struct timerequest *ti_TR;    /* Template timerequest */
  51. };
  52.  
  53. /* The TimerObject structure holds all the information specific to a */
  54. /* given timer event request.  It also contains pointers to a dispatch */
  55. /* function and the actual timer event request (along with a duplicate */
  56. /* timeval struct to reset the request in a TH_TICKING situation. */
  57. struct TimerObject
  58. {
  59.     struct timerequest ti_TR;    /* Embedded. */
  60.     struct timeval ti_TimeVal;    /* Embedded, holds the length to wait */
  61.     ULONG ti_FuncID;        /* The app FuncID to execute */
  62.     ULONG ti_Mode;        /* Either ticking or one-shot */
  63.     struct MHObject *ti_MHO;    /* Pointer back to parent */
  64. };
  65.  
  66. /* timer.c: function prototypes */
  67. BOOL open_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
  68. BOOL handle_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
  69. BOOL close_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
  70. BOOL shutdown_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
  71.  
  72. /* The following comment block is in Autodoc format.  This is a stringent
  73.  * standard, do NOT modify the format!!! */
  74.  
  75. /****** timer.lib/setup_timerA *********************************************
  76. *
  77. *    NAME
  78. *    setup_timerA - Initializes the timer message handler.
  79. *
  80. *    SYNOPSIS
  81. *    mh = setup_timerA (ai, tl)
  82. *    D0                 D0  D1
  83. *
  84. *    struct MsgHandler *mh;
  85. *    struct AppInfo *ai;
  86. *    struct TagItem *tl;
  87. *
  88. *    FUNCTION
  89. *    This is the low-level function used to initialize the timer message
  90. *    handler.
  91. *
  92. *    NOTE: This call should not be called directly by the application,
  93. *    but should invoked by using the APSH_AddHandler tag.
  94. *
  95. *    There are no tags recognized at startup time.
  96. *
  97. *    Valid tagitems to use at OPEN time:
  98. *
  99. *    APSH_TimerMode, <timer mode>
  100. *    This determines the what the handler returns to the user
  101. *    application.  Recognizes:
  102. *
  103. *          TH_TICKING      Repeated timer events, with frequency
  104. *                          based on content of APSH_TimerLen
  105. *
  106. *          TH_ONESHOT      One-shot alarm after duration given
  107. *                          in APSH_TimerLen
  108. *
  109. *    APSH_TimerLen,  <timeval>
  110. *    Sets the duration of the timer command.  Data is a pointer
  111. *    to a timeval structure (see timer.device).
  112. *
  113. *    APSH_CmdID,     <function ID>
  114. *    Function to execute when timer triggered.  Examine APSH_NameTag
  115. *    to get the ID code for the timer request.
  116. *
  117. *
  118. *    INPUTS
  119. *    ai  - Pointer to the application's AppInfo structure.
  120. *    tl  - Pointer to an array of TagItem attributes for the
  121. *          function.
  122. *
  123. *    RESULT
  124. *    mh  - Pointer to a MsgHandler structure.
  125. *
  126. *    BUGS
  127. *
  128. *    SEE ALSO
  129. *
  130. **************************************************************************
  131. *  Created:  21-May-91, John F Wiederhirn
  132. *
  133. */
  134.  
  135. struct MsgHandler * ASM
  136. setup_timerA (REG(d0) struct AppInfo * ai, REG(d1) struct TagItem * tl)
  137. {
  138.     struct MsgHandler *mh;
  139.     struct MHObject *mho;
  140.     struct TimerInfo *md;
  141.  
  142.     ULONG msize;
  143.  
  144.     /* This determines the amount of memory needed to hold the */
  145.     /* "anchor" information for a msg handler */
  146.     msize = sizeof (struct MsgHandler) +
  147.       sizeof (struct TimerInfo) +
  148.       (4L * sizeof (ULONG));
  149.  
  150.     /* Attempt to allocate the structure.  If it fails, fall */
  151.     /* through to a panic condition. */
  152.     if (mh = (struct MsgHandler *) AllocVec (msize, MEMFLAGS))
  153.     {
  154.     /* mho gets set to point to the embedded MHObject */
  155.     /* structure inside the MsgHandler struct we allocated... */
  156.     mho = &(mh->mh_Header);
  157.  
  158.     /* ...and the node embedded inside the MHObject struct */
  159.     /* needs to be set so that the system list handling */
  160.     /* functions can identify the owner. */
  161.     mho->mho_Node.ln_Type = APSH_MH_HANDLER_T;
  162.     mho->mho_Node.ln_Pri = APSH_MH_HANDLER_P;
  163.     mho->mho_Node.ln_Name = "TIMER";
  164.  
  165.     /* Initialize the list anchor.  All timer requests */
  166.     /* will be attached to this list anchor.  List is */
  167.     /* a standard doubly-linked Exec list. */
  168.     NewList (&(mho->mho_ObjList));
  169.  
  170.     /* This is just our personal message handler ID code */
  171.     /* so that AppShell can identify who is the owner of */
  172.     /* messages which get passed to it by Intuition. */
  173.     mho->mho_ID = APSH_TIMER_ID;
  174.  
  175.     /* get a pointer to the instance data */
  176.     mho->mho_SysData = md = MEMORY_FOLLOWING (mh);
  177.  
  178.     /* The array of function pointers is set up so that */
  179.     /* AppShell can dispatch messages as needed to the */
  180.     /* proper Timer Handler functions. */
  181.     mh->mh_NumFuncs = 4;
  182.     mh->mh_Func = MEMORY_FOLLOWING (md);
  183.     mh->mh_Func[APSH_MH_OPEN] = open_timer;
  184.     mh->mh_Func[APSH_MH_HANDLE] = handle_timer;
  185.     mh->mh_Func[APSH_MH_CLOSE] = close_timer;
  186.     mh->mh_Func[APSH_MH_SHUTDOWN] = shutdown_timer;
  187.  
  188.     /* Create a standard Exec IPC message port.  Since the */
  189.     /* port is private to the AppShell-based application */
  190.     /* and only AppShell will be sending messages to it, we */
  191.     /* don't need to bother giving it a name... */
  192.     if (mh->mh_Port = CreatePort (NULL, NULL))
  193.     {
  194.         /* ...but we do need to make sure that AppShell is */
  195.         /* aware of which bit is going to be the trigger for */
  196.         /* incoming message notification.  This is done by */
  197.         /* duplicating the mask of the port in the MsgHandler */
  198.         /* field designed for it. */
  199.         mh->mh_SigBits = (1L << mh->mh_Port->mp_SigBit);
  200.  
  201.         /* Create the template timerequest */
  202.         if (md->ti_TR = (struct timerequest *)
  203.         CreateExtIO (mh->mh_Port, sizeof (struct timerequest)))
  204.         {
  205.         /* Open the timer.device */
  206.         if (!(OpenDevice ("timer.device", NULL, (struct IORequest *) md->ti_TR, UNIT_WAITUNTIL)))
  207.         {
  208.             struct timerequest *treq = md->ti_TR;
  209.  
  210.             /* Also, the node embedded in the timerrequest needs */
  211.             /* to indicate the IPC port which should be notified. */
  212.             treq->tr_node.io_Message.mn_ReplyPort = mh->mh_Port;
  213.  
  214.             /* Set the command to tell the system a timer event */
  215.             /* is being requested, and to add it to the queue. */
  216.             treq->tr_node.io_Command = TR_ADDREQUEST;
  217.  
  218.             /* All the non-dynamic setup for the message handler */
  219.             /* is complete.  Return a pointer to the valid */
  220.             /* MsgHandler structure. */
  221.             return (mh);
  222.         }
  223.  
  224.         /* Delete the template timerequest */
  225.         DeleteExtIO ((struct IORequest *) md->ti_TR);
  226.         md->ti_TR = NULL;
  227.         }
  228.  
  229.         DeletePort (mh->mh_Port);
  230.         mh->mh_Port = NULL;
  231.  
  232.         /* Unable to allocate things */
  233.         ai->ai_Pri_Ret = RETURN_FAIL;
  234.         ai->ai_Sec_Ret = APSH_CLDNT_CREATE_PORT;
  235.         ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
  236.     }
  237.     else
  238.     {
  239.         /* The system was unable to allocate the IPC port. */
  240.         /* We set the proper flags to inform AppShell of the */
  241.         /* failure, and prepare the error message we want */
  242.         /* AppShell to display to the user (In this case, */
  243.         /* the stock "Timer Handler couldn't create port!" */
  244.         /* AppShell message). */
  245.         ai->ai_Pri_Ret = RETURN_FAIL;
  246.         ai->ai_Sec_Ret = APSH_CLDNT_CREATE_PORT;
  247.         ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
  248.     }
  249.  
  250.     /* The memory allocated for the MsgHandler structure can */
  251.     /* be deallocated now.  Just to make it clear, we also clear */
  252.     /* the mh and mho pointers for easy debugging. */
  253.     FreeVec ((APTR) mh);
  254.     mho = NULL;
  255.     mh = NULL;
  256.     }
  257.     else
  258.     {
  259.     /* Things are pretty bad.  The attempt to allocate the memory for */
  260.     /* the MsgHandler failed.  Chances are, the system will not be able */
  261.     /* to put up any requesters, but we set the AppShell flag to */
  262.     /* indicate we failed, and prepare the message (stock AppShell */
  263.     /* "Not Enough Memory!" message). */
  264.     ai->ai_Pri_Ret = RETURN_FAIL;
  265.     ai->ai_Sec_Ret = APSH_NOT_ENOUGH_MEMORY;
  266.     ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
  267.     }
  268.  
  269.     /* Return NULL to indicate failure just in case something besides */
  270.     /* AppShell is watching entry/exit conditions. */
  271.     return (NULL);
  272.  
  273. }
  274.  
  275. BOOL open_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
  276. {
  277.     struct MHObject *mho = &(mh->mh_Header);
  278.     struct TimerInfo *md = mho->mho_SysData;
  279.     struct TimerObject *tinf;
  280.     struct timerequest *treq;
  281.     struct MHObject *mob;
  282.     BOOL retval = FALSE;
  283.     struct timeval *tv;
  284.     STRPTR nam1, nam2;
  285.     ULONG mode, tsize;
  286.     LONG FuncID;
  287.  
  288.     /* The list of tags pointed to by the tl pointer contain the key-value */
  289.     /* pairs for the request to be queued.  This is done in an array of */
  290.     /* standard Exec TagItems. */
  291.  
  292.     /* The _NameTag TagItem needs to contain a pointer to a string */
  293.     /* which has the unique name for the request.  Since this is a */
  294.     /* required parameter, the default is NULL.  Also, because the */
  295.     /* "data side" of a TagItem key-value set is defined as a long, */
  296.     /* we need to cast it to a STRPTR. */
  297.     nam1 = (STRPTR) GetTagData (APSH_NameTag, NULL, tl);
  298.  
  299.     /* The _CmdID Tag contains the AppShell ID number for the Application */
  300.     /* function to be executed when the timer event occurs.  Again, the */
  301.     /* Tag is required so the default is NULL.  ID numbers happen to be */
  302.     /* longs as well, so no cast. */
  303.     FuncID = GetTagData (APSH_CmdID, NULL, tl);
  304.  
  305.     /* The last of the required Tags, the _TimerLen should contain a */
  306.     /* pointer to a standard timer.device timeval structure which sets */
  307.     /* the number of seconds and microseconds until the event. */
  308.     /* */
  309.     /* NOTE: Timing hasnt been saturation tested yet, so for the time */
  310.     /* being, this Handler is certified only for "second" granularity. */
  311.     tv = (struct timeval *) GetTagData (APSH_TimerLen, NULL, tl);
  312.  
  313.     /* The optional _TimerMode Tag contains a mode setting for the */
  314.     /* event to be queued.  The default value is TH_TICKING, which */
  315.     /* causes a recurrent event stream from the request.  TH_ONESHOT */
  316.     /* as mode would cause a single event from the request. */
  317.     mode = GetTagData (APSH_TimerMode, TH_TICKING, tl);
  318.  
  319.     /* This makes sure the required Tags were all present.  If not, */
  320.     /* the function simply returns a FALSE value. */
  321.     if (nam1 && FuncID && tv)
  322.     {
  323.     /* Here, the mode is checked to verify it falls within the */
  324.     /* range of acceptable values.  Failure case just falls */
  325.     /* through to a FALSE return right now. */
  326.     if ((mode > TH_STRTMODE) && (mode < TH_ENDMODE))
  327.     {
  328.         /* Calculate the amount of memory needed to hold all */
  329.         /* of the structs and data related to a given request... */
  330.         tsize = sizeof (struct MHObject) +
  331.           sizeof (struct TimerObject) +
  332.           strlen (nam1) + 1L;
  333.  
  334.         /* ...and allocate them. Failure simply falls through */
  335.         /* to a FALSE return currently.  This will eventually set */
  336.         /* up an error condition similar to the failed allocation */
  337.         /* error condition in setup_timerA.  The first structure */
  338.         /* is the MHObject so it gets the pointer returned from */
  339.         /* the allocation and casts it to the proper type. */
  340.         if (mob = (struct MHObject *) AllocVec (tsize, MEMFLAGS))
  341.         {
  342.         /* The rest of the structure pointers can now be aimed */
  343.         /* to the proper offsets inside the allocated block and */
  344.         /* cast to the proper type. */
  345.         tinf = (struct TimerObject *) MEMORY_FOLLOWING (mob);
  346.         tinf->ti_MHO = mob;
  347.         nam2 = (STRPTR) MEMORY_FOLLOWING (tinf);
  348.  
  349.         /* The embedded Exec node structure needs to be setup */
  350.         /* so the system knows who owns it.  The values get */
  351.         /* set to the assigned Timer Handler values. */
  352.         mob->mho_Node.ln_Type = APSH_MH_HANDLER_T;
  353.         mob->mho_Node.ln_Pri = APSH_MH_HANDLER_P;
  354.         mob->mho_Node.ln_Name = nam2;
  355.  
  356.         /* A field of the request's MHObject structure is set */
  357.         /* to point to the related TimerObject.  We need this */
  358.         /* later to find everything... */
  359.         mob->mho_SysData = tinf;
  360.  
  361.         /* Set the parent object */
  362.         mob->mho_Parent = mho;
  363.  
  364.         /* The MHObject is declared to belong to the Timer */
  365.         /* Handler, so AppShell can dispatch properly when. */
  366.         /* the event occurs. */
  367.         mob->mho_ID = APSH_TIMER_ID;
  368.  
  369.         /* Copy the contents pointed at by the _NameTag Tag */
  370.         /* into our own memory. */
  371.         strcpy (nam2, nam1);
  372.  
  373.         /* And finally, attach the request to the list of */
  374.         /* requests which are anchored by the MHObject that */
  375.         /* is embedded in the MsgHandler structure. */
  376.         AddTail (&(mho->mho_ObjList), &(mob->mho_Node));
  377.  
  378.         /* Copy over the values from the template (p.874 L&D RKM) */
  379.         tinf->ti_TR = *md->ti_TR;
  380.  
  381.         /* Get a pointer to the embedded timerequest */
  382.         treq = &(tinf->ti_TR);
  383.  
  384.         /* Initialize the timerequest structure with the */
  385.         /* values for the seconds and microseconds to wait */
  386.         /* until triggering the event. */
  387.         treq->tr_time.tv_secs = tinf->ti_TimeVal.tv_secs = tv->tv_secs;
  388.         treq->tr_time.tv_micro = tinf->ti_TimeVal.tv_micro = tv->tv_micro;
  389.  
  390.         /* Initialize the TimerObject structure with the */
  391.         /* information passed in the Tag array and where */
  392.         /* our handler should call to redispatch to the */
  393.         /* function given by the application. */
  394.         tinf->ti_Mode = mode;
  395.         tinf->ti_FuncID = FuncID;
  396.  
  397.         /* Everything has been intialized.  The request is */
  398.         /* placed in the system queue, and finally the retval */
  399.         /* var is changed so the function returns TRUE. */
  400.         SendIO ((struct IORequest *) treq);
  401.  
  402.         /* Increment the number of outstanding messages */
  403.         mh->mh_Outstanding++;
  404.  
  405.         retval = TRUE;
  406.         }
  407.     }
  408.     }
  409.  
  410.     /* Whatever the retval var holds at this point, that's what the */
  411.     /* AppShell dispatcher gets back. */
  412.     return (retval);
  413. }
  414.  
  415. BOOL handle_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
  416. {
  417.     struct MHObject *mho = &(mh->mh_Header);
  418.     struct TimerObject *tinf;
  419.     struct timerequest *treq;
  420.     struct timeval *tv;
  421.     LONG FuncID;
  422.  
  423.     /* Pull the messages from the port */
  424.     while (tinf = (struct TimerObject *) GetMsg (mh->mh_Port))
  425.     {
  426.     /* Decrement the number of outstanding messages */
  427.     mh->mh_Outstanding--;
  428.  
  429.     /* Set the current object */
  430.     mho->mho_CurNode = tinf->ti_MHO;
  431.  
  432.     /* This ugly little declaration/initialization just */
  433.     /* pulls the ID number of the application function to */
  434.     /* be called from where it was stashed in the TimerObject */
  435.     /* structure. */
  436.     FuncID = tinf->ti_FuncID;
  437.  
  438.     /* Since the function ID comes from the application, its */
  439.     /* not safe to assume it will be meaningful.  AppShell can */
  440.     /* handle the case where the function ID doesnt point to */
  441.     /* an existing app function, but this cannot be detected */
  442.     /* by the dispatcher so gets checked here.  NO_FUNCTION is */
  443.     /* the legal value for a null function table ID. */
  444.     if (FuncID != NO_FUNCTION)
  445.     {
  446.         /* This Tag array, like the FuncID var, doesnt need */
  447.         /* to hang around long, so local is fine. */
  448.         struct TagItem attrs[2];
  449.  
  450.         /* All these do is inform the dispatcher where the */
  451.         /* dispatch came from (a handler), and who owns it */
  452.         /* (the Timer handler). */
  453.         attrs[0].ti_Tag = APSH_Handler;
  454.         attrs[0].ti_Data = APSH_TIMER_ID;
  455.         attrs[1].ti_Tag = TAG_DONE;
  456.  
  457.         /* This is the entry point for the AppShell dispatcher */
  458.         /* for external dispatch requests.  It looks up the ID */
  459.         /* in its tables and dispatches to that function.  The */
  460.         /* NULL is because we are passing no command line along */
  461.         /* to the function. */
  462.         PerfFunc (ai, FuncID, NULL, NULL);
  463.     }
  464.  
  465.     /* treq to the timerequest struct stashed in the TimerObject... */
  466.     treq = (struct timerequest *) & (tinf->ti_TR);
  467.  
  468.     /* and tv to the timeval struct embedded in the TimerObject. */
  469.     tv = (struct timeval *) & (tinf->ti_TimeVal);
  470.  
  471.     /* When DispatchUser returns, this determines the mode so */
  472.     /* that the request can be requeued or terminated. */
  473.     switch (tinf->ti_Mode)
  474.     {
  475.         /* Just as it looks, requeue the request for another */
  476.         /* run.  Since the timer device trashes the values in */
  477.         /* the actual timerequest for how long to wait, they */
  478.         /* get re-initialized from the duplicate copies in the */
  479.         /* TimerObject structure.  Then the request is sent via */
  480.         /* the generic SendIO function back into the timer device */
  481.         /* queue. */
  482.         case TH_TICKING:
  483.         treq->tr_time.tv_secs = tv->tv_secs;
  484.         treq->tr_time.tv_micro = tv->tv_micro;
  485.         SendIO ((struct IORequest *) treq);
  486.  
  487.         /* Increment the number of outstanding messages */
  488.         mh->mh_Outstanding++;
  489.         break;
  490.  
  491.         /* In this case, the request can be terminated, so it */
  492.         /* gets passed to close_timer() for deallocation.  The node */
  493.         /* pointer gets reused here.  The two NULLs are to */
  494.         /* satify the prototype. */
  495.         case TH_ONESHOT:
  496.         close_timer (ai, mh, NULL);
  497.         break;
  498.     }
  499.     }
  500.  
  501.     /* Clear the current object, now that it is out there in the TwiZone */
  502.     mho->mho_CurNode = NULL;
  503.  
  504.     return (TRUE);
  505. }
  506.  
  507. BOOL close_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
  508. {
  509.     struct MHObject *mho = &(mh->mh_Header);
  510.     struct List *mhl = &(mho->mho_ObjList);
  511.     struct MHObject *mob = NULL;
  512.     struct TimerObject *tinf;
  513.     struct timerequest *treq;
  514.     struct timeval *tv;
  515.     BOOL retval = FALSE;
  516.     STRPTR nam1;
  517.  
  518.     /* See if they specified an event to close */
  519.     if (nam1 = (STRPTR) GetTagData (APSH_NameTag, NULL, tl))
  520.     {
  521.     /* Locate the node which contains the timer request. */
  522.     /* Since the node will always be the very first part */
  523.     /* of the MHObject it is related to, we can create a */
  524.     /* pointer to the MHObject with a simple cast. */
  525.     mob = (struct MHObject *) FindName (mhl, nam1);
  526.     }
  527.     /* No name was provided, so close the current object */
  528.     else
  529.     {
  530.     /* Get a pointer to the current object */
  531.     mob = mho->mho_CurNode;
  532.     }
  533.  
  534.     /* See if we have an object to close */
  535.     if (mob)
  536.     {
  537.     /* Once again, all the pointers need to be aimed at the proper */
  538.     /* elements of the request. */
  539.     tinf = (struct TimerObject *) mob->mho_SysData;
  540.     treq = (struct timerequest *) & (tinf->ti_TR);
  541.     tv = (struct timeval *) & (tinf->ti_TimeVal);
  542.  
  543.     /* Since this can be called by close_timer with the request */
  544.     /* still outstanding, test if it is. */
  545.     if (~CheckIO ((struct IORequest *) treq))
  546.     {
  547.         /* Decrement the number of outstanding messages */
  548.         mh->mh_Outstanding--;
  549.  
  550.         /* Its outstanding.  Use the generic AbortIO function */
  551.         /* to force the system to abort the request. */
  552.         AbortIO ((struct IORequest *) treq);
  553.  
  554.         /* This needs to wait until the system tells it that */
  555.         /* the request has been aborted. */
  556.         WaitIO ((struct IORequest *) treq);
  557.     }
  558.  
  559.     /* This unlinks the request from within our list of requests. */
  560.     Remove ((struct Node *) mob);
  561.  
  562.     /* And finally, all the memory allocated in open_timer can be given */
  563.     /* back to the system. */
  564.     FreeVec (mob);
  565.  
  566.     /* Indicate success */
  567.     retval = TRUE;
  568.     }
  569.  
  570.     return (retval);
  571. }
  572.  
  573. BOOL shutdown_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
  574. {
  575.     struct MHObject *mho = &(mh->mh_Header);
  576.     struct TimerInfo *md = mho->mho_SysData;
  577.     struct List *list = &(mho->mho_ObjList);
  578.     struct Node *node, *nxtnode;
  579.  
  580.     /* Remove the message handler from the AppShell list */
  581.     Remove ((struct Node *) mh);
  582.  
  583.     /* Get rid of any outstanding timer requests */
  584.  
  585.     /* Make sure there are entries in the list */
  586.     if (list->lh_TailPred != (struct Node *) list)
  587.     {
  588.     /* Let's start at the very beginning... */
  589.     node = list->lh_Head;
  590.  
  591.     /* Continue while there are still nodes */
  592.     while (nxtnode = node->ln_Succ)
  593.     {
  594.         /* Make this the current node */
  595.         mho->mho_CurNode = (struct MHObject *) node;
  596.  
  597.         /* Close the current node */
  598.         close_timer (ai, mh, NULL);
  599.  
  600.         /* Go on to the next node */
  601.         node = nxtnode;
  602.     }
  603.     }
  604.  
  605.     /* Close the timer device */
  606.     CloseDevice ((struct IORequest *) md->ti_TR);
  607.  
  608.     /* Delete the template timerequest */
  609.     DeleteExtIO ((struct IORequest *) md->ti_TR);
  610.  
  611.     /* At this point, the IPC port will receive no further */
  612.     /* messages from the timer device as all requests are */
  613.     /* deleted.  This implies all messages have been */
  614.     /* ReplyMsg'd as well, so its now safe to close the */
  615.     /* port. */
  616.     DeletePort (mh->mh_Port);
  617.  
  618.     /* Everything else is deallocated so this just returns */
  619.     /* the memory allocated in setup_timerA back to the */
  620.     /* system. */
  621.     FreeVec (mh);
  622.  
  623.     return (TRUE);
  624. }
  625.  
  626. /* END OF TIMER.C */
  627.